{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### Tensors\n",
    "\n",
    "Tensors 类似于 numpy 中的多维数组(ndarrays)，但它还可以用于 GPU 中实现计算的加速。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个未初始化的 5*3 矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[[0., 0.],\n",
      "         [0., 0.],\n",
      "         [0., 0.]],\n",
      "\n",
      "        [[0., 0.],\n",
      "         [0., 0.],\n",
      "         [0., 0.]],\n",
      "\n",
      "        [[0., 0.],\n",
      "         [0., 0.],\n",
      "         [0., 0.]],\n",
      "\n",
      "        [[0., 0.],\n",
      "         [0., 0.],\n",
      "         [0., 0.]]])\n"
     ]
    }
   ],
   "source": [
    "x = torch.empty(6, 3)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个随机初始化的 5*3 矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.5568, 0.7432, 0.0568],\n",
      "        [0.1352, 0.9693, 0.0482],\n",
      "        [0.4029, 0.2943, 0.3428],\n",
      "        [0.5108, 0.0529, 0.8496],\n",
      "        [0.1543, 0.9362, 0.4784]])\n"
     ]
    }
   ],
   "source": [
    "rand_x = torch.rand(5, 3)\n",
    "print(rand_x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个数值皆是 0，类型为 long 的矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0, 0, 0],\n",
      "        [0, 0, 0],\n",
      "        [0, 0, 0],\n",
      "        [0, 0, 0],\n",
      "        [0, 0, 0]])\n"
     ]
    }
   ],
   "source": [
    "zero_x = torch.zeros(5, 3, dtype=torch.long)\n",
    "print(zero_x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个 tensor，初始化方式是给定数值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([5.5000, 3.0000])\n"
     ]
    }
   ],
   "source": [
    "tensor1 = torch.tensor([5.5, 3])\n",
    "print(tensor1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据已存在的 tensor 创建新的 tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.]], dtype=torch.float64)\n"
     ]
    }
   ],
   "source": [
    "tensor2 = tensor1.new_ones(5, 3, dtype=torch.double)  # new_* 方法需要输入 tensor 大小\n",
    "print(tensor2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "修改数值类型，但有相同的 size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor3:  tensor([[ 1.4652,  0.2870, -1.3938],\n",
      "        [ 0.0108, -0.8733, -0.0456],\n",
      "        [-0.5516,  1.0508,  0.4572],\n",
      "        [ 0.3408, -0.7029,  0.2346],\n",
      "        [ 0.5659,  0.1279, -0.5390]])\n"
     ]
    }
   ],
   "source": [
    "tensor3 = torch.randn_like(tensor2, dtype=torch.float)\n",
    "print('tensor3: ', tensor3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "获取 tensor 的 size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([5, 3])\n"
     ]
    }
   ],
   "source": [
    "print(tensor3.size())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意**： torch.Size 实际上是元组(tuple)类型，所以支持所有的元组操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Operations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "加法操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor3 + tensor4=  tensor([[ 2.0666,  0.5750, -1.0611],\n",
      "        [ 0.3121, -0.1172,  0.5975],\n",
      "        [ 0.0143,  1.2622,  1.2131],\n",
      "        [ 0.6543,  0.2426,  0.6039],\n",
      "        [ 0.7276,  0.5528, -0.1790]])\n",
      "tensor3 + tensor4=  tensor([[ 2.0666,  0.5750, -1.0611],\n",
      "        [ 0.3121, -0.1172,  0.5975],\n",
      "        [ 0.0143,  1.2622,  1.2131],\n",
      "        [ 0.6543,  0.2426,  0.6039],\n",
      "        [ 0.7276,  0.5528, -0.1790]])\n"
     ]
    }
   ],
   "source": [
    "tensor4 = torch.rand(5, 3)\n",
    "print('tensor3 + tensor4= ', tensor3 + tensor4)\n",
    "print('tensor3 + tensor4= ', torch.add(tensor3, tensor4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "新声明一个 tensor 变量保存加法操作的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "add result=  tensor([[ 2.6679,  0.8630, -0.7283],\n",
      "        [ 0.6134,  0.6389,  1.2407],\n",
      "        [ 0.5801,  1.4737,  1.9690],\n",
      "        [ 0.9678,  1.1881,  0.9732],\n",
      "        [ 0.8894,  0.9776,  0.1810]])\n"
     ]
    }
   ],
   "source": [
    "result = torch.empty(5, 3)\n",
    "torch.add(tensor3, tensor4, out=result)\n",
    "print('add result= ', result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "直接修改变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor3=  tensor([[ 2.0666,  0.5750, -1.0611],\n",
      "        [ 0.3121, -0.1172,  0.5975],\n",
      "        [ 0.0143,  1.2622,  1.2131],\n",
      "        [ 0.6543,  0.2426,  0.6039],\n",
      "        [ 0.7276,  0.5528, -0.1790]])\n"
     ]
    }
   ],
   "source": [
    "tensor3.add_(tensor4)\n",
    "print('tensor3= ', tensor3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意**：可以改变 tensor 变量的操作都带有一个后缀 `_`, 例如 x.copy_(y), x.t_() 都可以改变 x 变量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以用类似 numpy 的索引操作来访问 tensor 的某一维, 比如访问 tensor3 第一列数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([2.0666, 0.3121, 0.0143, 0.6543, 0.7276])\n"
     ]
    }
   ],
   "source": [
    "print(tensor3[:, 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "修改 tensor 大小，可以采用方法 `torch.view`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(4, 4)\n",
    "y = x.view(16)\n",
    "# -1 表示除给定维度外的其余维度的乘积\n",
    "z = x.view(-1, 8)\n",
    "print(x.size(), y.size(), z.size())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果 tensor 仅有一个元素，可以采用 `.item()` 来获取类似 Python 中整数类型的数值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([0.2602])\n",
      "0.26017212867736816\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(1)\n",
    "print(x)\n",
    "print(x.item())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Numpy\n",
    "\n",
    "tensor 和 numpy 的 array 相互转换，并且如果是在 cpu 上的操作，两者转换会共享内存，即改变一个变量同时也改变另一个。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Tensor to Numpy array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1., 1., 1., 1., 1.])\n"
     ]
    }
   ],
   "source": [
    "a = torch.ones(5)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1. 1. 1. 1. 1.]\n"
     ]
    }
   ],
   "source": [
    "b = a.numpy()\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看看改变其中一个变量，另一个变量的改变"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([2., 2., 2., 2., 2.])\n",
      "[2. 2. 2. 2. 2.]\n"
     ]
    }
   ],
   "source": [
    "a.add_(1)\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Numpy Array to Torch Tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2. 2. 2. 2. 2.]\n",
      "tensor([2., 2., 2., 2., 2.], dtype=torch.float64)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "a = np.ones(5)\n",
    "b = torch.from_numpy(a)\n",
    "np.add(a, 1, out=a)\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 CPU 上，除了 CharTensor 外，都支持和 Numpy 的 array 的互相转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### CUDA Tensors\n",
    "\n",
    "Tensors 可以通过采用 `.to` 方法来转换到不同设备上"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1.2602], device='cuda:0')\n",
      "tensor([1.2602], dtype=torch.float64)\n"
     ]
    }
   ],
   "source": [
    "# 当 CUDA 可用的时候，可运行这段代码，采用 torch.device() 来改变 tensors 是否在 GPU 上进行计算操作\n",
    "if torch.cuda.is_available():\n",
    "    device = torch.device(\"cuda\")          # 定义一个 CUDA 设备对象\n",
    "    y = torch.ones_like(x, device=device)  # 显示创建在 GPU 上的一个 tensor\n",
    "    x = x.to(device)                       # 也可以采用 .to(\"cuda\")\n",
    "    z = x + y\n",
    "    print(z)\n",
    "    print(z.to(\"cpu\", torch.double))       # .to() 方法也可以改变数值类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
